package com.baize.cpdepus.service.lmp;


import com.baize.cpdepus.common.OssService;
import com.baize.cpdepus.common.RedisCache;
import com.baize.cpdepus.common.UploadingOssService;

import com.baize.cpdepus.entity.LoginUser;
import com.baize.cpdepus.entity.User;
import com.baize.cpdepus.exception.ExceptionEnum;
import com.baize.cpdepus.exception.ServiceException;
import com.baize.cpdepus.mapper.UserMapper;
import com.baize.cpdepus.service.UserService;
import com.baize.cpdepus.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;

import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.Objects;
import java.util.UUID;

@Slf4j
@Service
@Transactional
public class UserServiceImp implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private PasswordEncoder PasswordEncoder;

    /**
     * 在接口中我们通过AuthenticationManager的authenticate方法来进行用户认证,所以需要在SecurityConfig中配置把AuthenticationManager注入容器。
     * 认证成功的话要生成一个jwt，放入响应中返回。并且为了让用户下回请求时能通过jwt识别出具体的是哪个用户，我们需要把用户信息存入redis，可以把用户id作为key。
     */
    @Override
    public String login(User usr) {
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(usr.getPhone(), usr.getPassword());
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        if (Objects.isNull(authenticate)) {
            throw new RuntimeException("用户名或密码错误");
        }
        LoginUser principal = (LoginUser) authenticate.getPrincipal();
        String userId = principal.getUser().getId().toString();
        redisCache.setCacheObject("login:" + userId, principal);
        return JwtUtil.createJWT(userId);
    }

    @Override
    public User getById(Integer id) {
        User user;
        LoginUser cacheObject = redisCache.getCacheObject("login:" + id);
        if (Objects.nonNull(cacheObject)) {
            user = cacheObject.getUser();
        } else {
            user = userMapper.SelectById(id);
        }

        return user;
    }

    @Override
    public void logout() {
        UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        Integer userid = loginUser.getUser().getId();
        redisCache.deleteObject("login:" + userid);
    }

    //TODO 方法的异常处理需要整改
    @Override
    public void AddOne(User user, MultipartFile file) {
        if (Objects.nonNull(userMapper.SelectOne(user.getPhone()))) {
            throw new ServiceException(ExceptionEnum.USER_MEMORY);
        }
        {
            int uuid = UUID.randomUUID().toString().replaceAll("-", "").hashCode();
            uuid = uuid < 0 ? -uuid : uuid;//String.hashCode() 值会为空
            user.setId(uuid);
            if (user.getName() == null) user.setName(user.getPhone().toString());
            String encode = PasswordEncoder.encode(user.getPassword());
            user.setPassword(encode);
        }
        if (Objects.isNull(file)) {
            userMapper.InsertOne(user);
            return;
        }
        try {
            String portrait = UploadingOssService.addOss(file);//将头像添加到oss；
            user.setPortrait(portrait);
            userMapper.InsertOne(user);
        } catch (IOException e) {
            //文件上传失败，作为头像的图片上传失败了，可以继续注册不影响整个项目的继续运行
            throw new ServiceException(ExceptionEnum.FILE_ERROR);
        }
    }

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void UpdateOne(User user, MultipartFile file) {

        try {
            if (Objects.nonNull(file)) {
                user.setPortrait(UploadingOssService.addOss(file));
            }
            //修改User信息之后，现在还是登入状态，需要修改redis中的信息，或者删除redis中的信息直接查询mysql
            userMapper.UpdateOne(user);

        } catch (Exception e) {
            throw new ServiceException(ExceptionEnum.UPDATE_ERROR);
        }


    }

    @Override
    public void UpdatePasswrod(User user) {
        try {
            userMapper.UpdatePasswrod(user);
        } catch (Exception e) {
              throw  new ServiceException(ExceptionEnum.UPDATE_ERROR);
        }

    }

    @Override
    public void Delete(Integer ID) {
        OssService.deleteFile(userMapper.SelectById(ID).getPortrait());
        userMapper.Delete(ID);

    }


    @Override
    @Transactional(propagation = Propagation.SUPPORTS)
    public User getOne(Long phone) {
        User user = userMapper.SelectOne(phone);
        return userMapper.SelectOne(phone);
    }


}
